package com.insane.web.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;

import com.insane.web.common.Page;

@SuppressWarnings({ "unchecked", "rawtypes" })
public class BaseDao<T, PK extends Serializable> {

	@Autowired
	private SessionFactory sessionFactory;

	protected Class<T> clazz;

	public BaseDao() {
		Type t = getClass().getGenericSuperclass();
		if (t instanceof ParameterizedType) {
			ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
			this.clazz = (Class) type.getActualTypeArguments()[0];
		}
	}

	public PK save(T entity) {
		return (PK) getSession().save(entity);
	}

	public void saveOrUpdate(T entity) {
		getSession().saveOrUpdate(entity);
	}

	public void update(T entity) {
		getSession().update(entity);
	}

	public void delete(PK id) {
		getSession().delete(this.get(id));
	}

	public void deleteObject(T entity) {
		getSession().delete(entity);
	}

	public boolean exists(PK id) {
		return get(id) != null;
	}

	public T load(PK id) {
		return (T) getSession().load(this.clazz, id);
	}

	public T get(PK id) {
		return (T) getSession().get(this.clazz, id);
	}

	public void deleteAll(Collection<?> entities) {
		if (entities == null)
			return;
		for (Object entity : entities) {
			getSession().delete(entity);
		}
	}

	public void saveAll(Collection<?> entities) {
		if (entities == null)
			return;
		for (Object entity : entities) {
			getSession().save(entity);
		}
	}

	public List<T> getAll() {
		CriteriaBuilder builder = createCriteriaBuilder();
		CriteriaQuery<T> criteria = builder.createQuery(this.clazz);
		Root<T> root = criteria.from(this.clazz);
		criteria.select(root);
		return getSession().createQuery(criteria).getResultList();
	}

	public void getByPage(Page page, String sql, Object queryParams) {
		String countSql = "select count(1) from (" + sql + ") hs_c";
		Query countQuery = createNativeQuery(countSql);
		setParameter(countQuery, queryParams);
		Long totalCount = Long.parseLong(countQuery.getSingleResult().toString());
		page.setTotal(totalCount);
		if (totalCount > 0) {
			Query<?> dataQuery = null;
			if(page.getClazz() != null){
				dataQuery = createNativeQuery(sql,page.getClazz());
			}else{
				dataQuery = createNativeQuery(sql);
			}
			setParameter(dataQuery, queryParams);
			page.setRows(dataQuery.getResultList());
		}
	}

	public CriteriaBuilder createCriteriaBuilder() {
		return getSession().getCriteriaBuilder();
	}

	public Query createQuery(String hql) {
		return getSession().createQuery(hql);
	}

	public Query createQuery(String hql, Class resultType) {
		return getSession().createQuery(hql, resultType);
	}
	
	public Query createNativeQuery(String sql){
		return getSession().createNativeQuery(sql);
	}
	
	public Query createNativeQuery(String sql, Class resultType){
		return getSession().createNativeQuery(sql, resultType);
	}

	protected Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	private void setParameter(Query query, Object queryParams) {
		if (queryParams != null) {
			if (queryParams instanceof Object[]) {
				Object[] params = (Object[]) queryParams;
				if (params.length > 0) {
					for (int i = 0; i < params.length; i++) {
						query.setParameter(i + 1, params[i]);
					}
				}
			} else if (queryParams instanceof Map) {
				Map params = (Map) queryParams;
				Iterator<String> it = params.keySet().iterator();
				while (it.hasNext()) {
					String key = it.next();
					query.setParameter(key, params.get(key));
				}
			}
		}
	}
}
